package com.wql.plugin;

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.mybatis.generator.api.CommentGenerator;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.JavaVisibility;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.Parameter;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.api.dom.xml.Element;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.codegen.mybatis3.ListUtilities;
import org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities;
import org.mybatis.generator.config.GeneratedKey;

public class EnhanceSelectSqlPlugin extends PluginAdapter {
	public boolean validate(List<String> warnings) {
		return true;
	}

	// 修改javamapper和sqlmapper终查询语句的名称
	public void initialized(IntrospectedTable introspectedTable) {
		introspectedTable
				.setExampleType(introspectedTable.getBaseRecordType() + getProperty("EnhanceSqlParamClassExt"));
		introspectedTable.setMyBatis3UpdateByExampleWhereClauseId(getProperty("EnhanceSqlMapperId_WhereForUpdate"));
		introspectedTable.setExampleWhereClauseId(getProperty("EnhanceSqlMapperId_Where"));
		introspectedTable
				.setUpdateByExampleWithBLOBsStatementId(getProperty("EnhanceSqlMapperId_UpdateByParamWithBlobs"));
		introspectedTable
				.setUpdateByExampleSelectiveStatementId(getProperty("EnhanceSqlMapperId_UpdateByParamSelective"));
		introspectedTable
				.setSelectByExampleWithBLOBsStatementId(getProperty("EnhanceSqlMapperId_SelectByParamWithBlobs"));
		introspectedTable.setUpdateByExampleStatementId(getProperty("EnhanceSqlMapperId_UpdateByParam"));
		introspectedTable.setSelectByExampleStatementId(getProperty("EnhanceSqlMapperId_SelectByParam"));
		introspectedTable.setDeleteByExampleStatementId(getProperty("EnhanceSqlMapperId_DeleteByParam"));
		introspectedTable.setCountByExampleStatementId(getProperty("EnhanceSqlMapperId_CountByParam"));
	}

	// 修改mapper接口类中参数名为example的参数为指定名称
	public boolean clientUpdateByExampleWithoutBLOBsMethodGenerated(Method method, Interface interfaze,
			IntrospectedTable introspectedTable) {
		exampleMapperParamNameModify(method);
		return true;
	}

	// 修改mapper接口类中参数名为example的参数为指定名称
	public boolean clientUpdateByExampleSelectiveMethodGenerated(Method method, Interface interfaze,
			IntrospectedTable introspectedTable) {
		exampleMapperParamNameModify(method);
		return true;
	}

	// 修改mapper接口类中参数名为example的参数为指定名称
	public boolean clientUpdateByExampleWithBLOBsMethodGenerated(Method method, Interface interfaze,
			IntrospectedTable introspectedTable) {
		exampleMapperParamNameModify(method);
		return true;
	}

	// 修改mapper接口类中参数名为example的参数为指定名称
	public boolean clientSelectByExampleWithBLOBsMethodGenerated(Method method, Interface interfaze,
			IntrospectedTable introspectedTable) {
		exampleMapperParamNameModify(method);
		return true;
	}

	// 修改mapper接口类中参数名为example的参数为指定名称
	public boolean clientSelectByExampleWithoutBLOBsMethodGenerated(Method method, Interface interfaze,
			IntrospectedTable introspectedTable) {
		exampleMapperParamNameModify(method);
		return true;
	}

	// 修改mapper接口类中参数名为example的参数为指定名称
	public boolean clientCountByExampleMethodGenerated(Method method, Interface interfaze,
			IntrospectedTable introspectedTable) {
		exampleMapperParamNameModify(method);
		return true;
	}

	// 修改mapper接口类中参数名为example的参数为指定名称
	public boolean clientDeleteByExampleMethodGenerated(Method method, Interface interfaze,
			IntrospectedTable introspectedTable) {
		exampleMapperParamNameModify(method);
		return true;
	}

	// insert数据返回主键
	public boolean sqlMapInsertElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
		addInsertResultPrimaryKey(element, introspectedTable);
		return true;
	}

	// insert数据返回主键
	public boolean sqlMapInsertSelectiveElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
		addInsertResultPrimaryKey(element, introspectedTable);
		return true;
	}

	// sqlmapper增加limit查询字段
	public boolean sqlMapSelectByExampleWithBLOBsElementGenerated(XmlElement element,
			IntrospectedTable introspectedTable) {
		sqlmapperAddLimit(element);
		return true;
	}

	// sqlmapper增加limit查询字段
	public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated(XmlElement element,
			IntrospectedTable introspectedTable) {
		sqlmapperAddLimit(element);
		return true;
	}

	// sqlmapper中where字段中的example参数修改
	public boolean sqlMapExampleWhereClauseElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
		String key = getProperty("EnhanceSqlMapperId_UpdateByParamParamkey");
		if (!key.isEmpty()) {
			XmlElement foreach = getChild(element, Arrays.asList(new String[] { "where", "foreach" }));
			if (null != foreach) {
				for (Attribute attribute : foreach.getAttributes()) {
					if (attribute.getName().equals("collection")) {
						String value = attribute.getValue();
						if (value.startsWith("example.")) {
							try {
								java.lang.reflect.Field field = attribute.getClass().getDeclaredField("value");
								field.setAccessible(true);
								field.set(attribute, value.replace("example", key));
							} catch (Exception e) {
								e.printStackTrace();
							}
						}
					}
				}
			}
		}
		return true;
	}

	// 查询参数类增加limit字段
	public boolean modelExampleClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		// 增加分页查询
		String paging = getProperty("EnhanceSqlParamPaging");
		String limit = getProperty("EnhanceSqlParamLimit");
		if (!paging.isEmpty()) {
			topLevelClass.addImportedType(paging);
			topLevelClass.setSuperClass(paging.substring(paging.lastIndexOf(".") + 1));
		}
		if (!limit.isEmpty()) {
			CommentGenerator commentGenerator = context.getCommentGenerator();
			Field field = new Field();
			field.setVisibility(JavaVisibility.PROTECTED);
			field.setType(FullyQualifiedJavaType.getStringInstance());
			field.setName("limit");
			commentGenerator.addFieldComment(field, introspectedTable);
			topLevelClass.addField(field);

			Method method = new Method();
			method.setVisibility(JavaVisibility.PUBLIC);
			method.setName("setLimit");
			method.addParameter(new Parameter(FullyQualifiedJavaType.getStringInstance(), "limit"));
			method.addBodyLine("this.limit = limit;");
			commentGenerator.addGeneralMethodComment(method, introspectedTable);
			topLevelClass.addMethod(method);

			method = new Method();
			method.setVisibility(JavaVisibility.PUBLIC);
			method.setReturnType(FullyQualifiedJavaType.getStringInstance());
			method.setName("getLimit");
			method.addBodyLine("return limit;");
			commentGenerator.addGeneralMethodComment(method, introspectedTable);
			topLevelClass.addMethod(method);
		}
		return true;
	}

	// mapper类增加动态批量插入接口
	public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass,
			IntrospectedTable introspectedTable) {
		String key = getProperty("EnhanceSqlMapperId_InsertBatchBySelective");
		if (!key.isEmpty()) {
			Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
			Method method = new Method();

			method.setReturnType(FullyQualifiedJavaType.getIntInstance());
			method.setVisibility(JavaVisibility.PUBLIC);
			method.setName(key);

			FullyQualifiedJavaType list = new FullyQualifiedJavaType("java.util.List");
			list.addTypeArgument(introspectedTable.getRules().calculateAllFieldsClass());
			method.addParameter(new Parameter(list, "records", "@Param(\"records\")"));
			importedTypes.add(list);

			FullyQualifiedJavaType parameterType = introspectedTable.getRules().calculateAllFieldsClass();
			method.addParameter(new Parameter(parameterType, "record", "@Param(\"record\")"));
			importedTypes.add(parameterType);

			importedTypes.add(new FullyQualifiedJavaType("org.apache.ibatis.annotations.Param"));

			context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

			interfaze.addImportedTypes(importedTypes);
			interfaze.addMethod(method);
		}

		return true;
	}

	// mapper文件增加动态批量插入sql
	public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {
		String key = getProperty("EnhanceSqlMapperId_InsertBatchBySelective");
		if (!key.isEmpty()) {

			XmlElement answer = new XmlElement("insert");

			answer.addAttribute(new Attribute("id", key));

			answer.addAttribute(new Attribute("parameterType", "map"));

			context.getCommentGenerator().addComment(answer);

			GeneratedKey gk = introspectedTable.getGeneratedKey();
			if (gk != null) {
				IntrospectedColumn introspectedColumn = introspectedTable.getColumn(gk.getColumn());
				if (introspectedColumn != null) {
					if (gk.isJdbcStandard()) {
						answer.addAttribute(new Attribute("useGeneratedKeys", "true"));
						answer.addAttribute(new Attribute("keyProperty", introspectedColumn.getJavaProperty()));
						answer.addAttribute(new Attribute("keyColumn", introspectedColumn.getActualColumnName()));
					} else {
						String identityColumnType = introspectedColumn.getFullyQualifiedJavaType()
								.getFullyQualifiedName();
						XmlElement selectKey = new XmlElement("selectKey");
						selectKey.addAttribute(new Attribute("resultType", identityColumnType));
						selectKey.addAttribute(new Attribute("keyProperty", introspectedColumn.getJavaProperty()));
						selectKey.addAttribute(new Attribute("order", gk.getMyBatis3Order()));
						selectKey.addElement(new TextElement(gk.getRuntimeSqlStatement()));
						answer.addElement(selectKey);
					}
				}
			}
			StringBuilder sb = new StringBuilder();
			sb.append("insert into ").append(introspectedTable.getFullyQualifiedTableNameAtRuntime());
			answer.addElement(new TextElement(sb.toString()));

			XmlElement insertTrimElement = new XmlElement("trim");
			insertTrimElement.addAttribute(new Attribute("prefix", "("));
			insertTrimElement.addAttribute(new Attribute("suffix", ")"));
			insertTrimElement.addAttribute(new Attribute("suffixOverrides", ","));
			answer.addElement(insertTrimElement);

			XmlElement foreachElement = new XmlElement("foreach");
			foreachElement.addAttribute(new Attribute("collection", "records"));
			foreachElement.addAttribute(new Attribute("item", "item"));
			foreachElement.addAttribute(new Attribute("index", "index"));
			foreachElement.addAttribute(new Attribute("open", "values "));
			foreachElement.addAttribute(new Attribute("separator", ","));
			foreachElement.addAttribute(new Attribute("close", ""));
			answer.addElement(foreachElement);

			XmlElement valuesTrimElement = new XmlElement("trim");
			valuesTrimElement.addAttribute(new Attribute("prefix", "("));
			valuesTrimElement.addAttribute(new Attribute("suffix", ")"));
			valuesTrimElement.addAttribute(new Attribute("suffixOverrides", ","));
			foreachElement.addElement(valuesTrimElement);

			for (IntrospectedColumn introspectedColumn : ListUtilities
					.removeIdentityAndGeneratedAlwaysColumns(introspectedTable.getAllColumns())) {

				XmlElement insertNotNullElement = new XmlElement("if");
				sb.setLength(0);
				sb.append("record.").append(introspectedColumn.getJavaProperty()).append(" != null");
				insertNotNullElement.addAttribute(new Attribute("test", sb.toString()));

				sb.setLength(0);
				sb.append(MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn)).append(',');
				insertNotNullElement.addElement(new TextElement(sb.toString()));
				insertTrimElement.addElement(insertNotNullElement);

				XmlElement valuesNotNullElement = new XmlElement("if");
				sb.setLength(0);
				sb.append("item.").append(introspectedColumn.getJavaProperty()).append(" != null");
				valuesNotNullElement.addAttribute(new Attribute("test", sb.toString()));

				sb.setLength(0);
				sb.append(MyBatis3FormattingUtilities.getParameterClause(introspectedColumn, "item."));
				sb.append(',');
				valuesNotNullElement.addElement(new TextElement(sb.toString()));
				valuesTrimElement.addElement(valuesNotNullElement);
			}
			document.getRootElement().addElement(answer);
		}
		return true;
	}

	// 实体类增加toString方法
	public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		String key = getProperty("EnhanceSqlModelToString");
		if (!key.isEmpty() && "true".equalsIgnoreCase(key)) {
			Method method = new Method();
			method.setReturnType(FullyQualifiedJavaType.getStringInstance());
			method.setVisibility(JavaVisibility.PUBLIC);
			method.setName("toString");
			context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);
			StringBuilder sb = new StringBuilder("return \"");
			sb.append(introspectedTable.getBaseRecordType()
					.substring(introspectedTable.getBaseRecordType().lastIndexOf(".") + 1)).append(" [");
			for (IntrospectedColumn column : introspectedTable.getNonBLOBColumns()) {
				sb.append(column.getJavaProperty()).append("=\" + ").append(column.getJavaProperty()).append(" + \", ");
			}
			sb.delete(sb.length() - 2, sb.length());
			sb.append("]\";");
			method.addBodyLine(sb.toString());
			topLevelClass.addMethod(method);
		}
		return true;
	}

	// 实体类增加toString方法
	public boolean modelRecordWithBLOBsClassGenerated(TopLevelClass topLevelClass,
			IntrospectedTable introspectedTable) {
		String key = getProperty("EnhanceSqlModelToString");
		if (!key.isEmpty() && "true".equalsIgnoreCase(key)) {
			Method method = new Method();
			method.setReturnType(FullyQualifiedJavaType.getStringInstance());
			method.setVisibility(JavaVisibility.PUBLIC);
			method.setName("toString");
			context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);
			StringBuilder sb = new StringBuilder("return \"");
			sb.append(introspectedTable.getRecordWithBLOBsType()
					.substring(introspectedTable.getRecordWithBLOBsType().lastIndexOf(".") + 1)).append(" [");
			for (IntrospectedColumn column : introspectedTable.getBLOBColumns()) {
				sb.append(column.getJavaProperty()).append("=\" + Arrays.toString(").append(column.getJavaProperty())
						.append(") + \", ");
			}
			sb.append("toString()=\" + super.toString() + \"]\";");
			method.addBodyLine(sb.toString());
			Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
			importedTypes.add(new FullyQualifiedJavaType("java.util.Arrays"));
			topLevelClass.addImportedTypes(importedTypes);
			topLevelClass.addMethod(method);
		}
		return true;
	}

	// sqlmapper增加limit查询字段
	private void sqlmapperAddLimit(XmlElement element) {
		String paging = getProperty("EnhanceSqlParamPaging");
		if (!paging.isEmpty()) {
			XmlElement ifElement = new XmlElement("if");
			ifElement.addAttribute(new Attribute("test", "limit != null"));
			ifElement.addElement(new TextElement("limit ${limit}"));
			element.addElement(ifElement);
		}
	}

	// insert后返回主键
	private void addInsertResultPrimaryKey(XmlElement element, IntrospectedTable introspectedTable) {
		String key = getProperty("EnhanceSqlResultPrimaryKey");
		if (!key.isEmpty() && "true".equalsIgnoreCase(key)) {
			List<IntrospectedColumn> primaryKeyColumns = introspectedTable.getPrimaryKeyColumns();
			if (null != primaryKeyColumns && !primaryKeyColumns.isEmpty()) {
				for (IntrospectedColumn primaryKey : primaryKeyColumns) {
					if (primaryKey.isAutoIncrement()) {
						element.addAttribute(new Attribute("useGeneratedKeys", "true"));
						element.addAttribute(new Attribute("keyProperty", primaryKey.getJavaProperty()));
						element.addAttribute(new Attribute("keyColumn", primaryKey.getActualColumnName()));
						break;
					}
				}
			}
		}
	}

	// 修改mapper接口类中参数名为example的参数为指定名称
	private void exampleMapperParamNameModify(Method method) {
		String key = getProperty("EnhanceSqlMapperId_UpdateByParamParamkey");
		if (!key.isEmpty()) {
			for (Parameter parameter : method.getParameters()) {
				if (parameter.getName().equalsIgnoreCase("example")) {
					try {
						java.lang.reflect.Field field = parameter.getClass().getDeclaredField("name");
						field.setAccessible(true);
						field.set(parameter, parameter.getName().replace("example", key));
					} catch (Exception e) {
						e.printStackTrace();
					}
					for (int i = parameter.getAnnotations().size() - 1; i > -1; i--) {
						String annotation = parameter.getAnnotations().get(i);
						if (annotation.contains("example")) {
							parameter.getAnnotations().set(i, annotation.replace("example", key));
							break;
						}
					}
				}
			}
		}
	}

	// 遍历子节点，获取指定节点
	private XmlElement getChild(XmlElement element, List<String> targetNameList) {
		if (null != element && null != targetNameList && !targetNameList.isEmpty()) {
			for (int i = element.getElements().size() - 1; i > -1; i--) {
				Element el = element.getElements().get(i);
				if (el instanceof XmlElement) {
					XmlElement target = (XmlElement) el;
					if (target.getName().equalsIgnoreCase(targetNameList.get(0))) {
						if (targetNameList.size() > 1) {
							return getChild(target, targetNameList.subList(1, targetNameList.size()));
						} else {
							return target;
						}
					}
				}
			}
		}
		return null;
	}

	private String getProperty(String key) {
		String value = this.properties.getProperty(key);
		if (null == value || value.startsWith("$")) {
			return "";
		}
		return value.trim();
	}

}
